using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace tutFuelCellGame
{
    class FuelCarrier : GameObject
    {
        public float ForwardDirection { get; set; }
        public float TurnImpulse { get; set; }
        public float Thrust { get; set; }
        public float Roll { get; set; }
        public int MaxRange { get; set; }

        public float Height
        {
            get { return Position.Y; }
            set { Position = new Vector3(Position.X, value, Position.Z); }
        }

        public FuelCarrier()
            : base()
        {
            ForwardDirection = 0.0f;
            TurnImpulse = 0f;
            Thrust = 0f;
            Roll = 0f;
            ShadowAlpha = 0.2f;
            MaxRange = GameConstants.MaxRange;
        }

        public void LoadContent(ContentManager content, string modelName)
        {
            Model = content.Load<Model>(modelName);
            BoundingSphere = CalculateBoundingSphere(GameConstants.FuelCarrierBoundingSphereFactor);
        }

        public void Draw(Matrix view, Matrix projection)
        {
            Matrix worldMatrix = GetWorldMatrix();

            DrawModelMeshes(worldMatrix, view, projection);
        }

        public void DrawShadow(Matrix view, Matrix projection, Matrix shadow)
        {
            Matrix worldMatrix = GetWorldMatrix();

            DrawModelMeshesWithShadow(worldMatrix, view, projection, shadow);
        }

        private Matrix GetWorldMatrix()
        {
            Matrix rotationYMatrix = Matrix.CreateRotationY(ForwardDirection);
            Matrix rotationZMatrix = Matrix.CreateRotationZ(Roll);
            Matrix translateMatrix = Matrix.CreateTranslation(Position);
            Matrix scaleMatrix = Matrix.CreateScale(Scale);
            return scaleMatrix * rotationZMatrix * rotationYMatrix * translateMatrix;
        }

        public void Update(InputManager inputs, Barriers barriers, GameState gameState, TimeSpan elapsedTime)
        {
            float elapsedSeconds = (float)elapsedTime.TotalSeconds;

            if (gameState == GameState.Running || gameState == GameState.Lost)
            {
                float amountToTurn = inputs.TurnAmount * elapsedSeconds * 0.153f;
                // adjust turning radius for speed
                amountToTurn *= (1 - (Thrust / GameConstants.MaxVelocity) * 0.2f);
                TurnImpulse += amountToTurn;
            }

            if (inputs.TurnAmount == 0)
                TurnImpulse = ApplyDrag(TurnImpulse, GameConstants.DragOnTurnImpulse * elapsedSeconds);
            TurnImpulse = MathHelper.Clamp(TurnImpulse, -GameConstants.MaxRotationSpeed, GameConstants.MaxRotationSpeed);

            ForwardDirection += TurnImpulse;

            Roll = -TurnImpulse * elapsedSeconds* 20f / GameConstants.MaxRotationSpeed;
            Roll = MathHelper.Clamp(Roll, MathHelper.ToRadians(-20), MathHelper.ToRadians(20));

            Matrix orientationMatrix = Matrix.CreateRotationY(ForwardDirection);

            Vector3 movement = Vector3.Zero;
            if (gameState == GameState.Running)
            {
                Thrust += inputs.Thrust * elapsedSeconds;
            }
            Thrust = MathHelper.Clamp(Thrust, -GameConstants.MaxVelocity, GameConstants.MaxVelocity);
            if (inputs.Thrust == 0f)
                Thrust = ApplyDrag(Thrust, GameConstants.Drag * elapsedSeconds);

            movement.Z = Thrust;
            Vector3 speed = Vector3.Transform(movement, orientationMatrix);

            Vector3 futurePosition = Position + speed;
            if (gameState == GameState.Running)
            {
                Vector3 BoundingfuturePosition = futurePosition;
                BoundingfuturePosition.Y = 0;
                BoundingSphere = new BoundingSphere(BoundingfuturePosition, BoundingSphere.Radius);

                if (ValidateMovement(futurePosition, barriers))
                {
                    Position = futurePosition;
                }
            }
            else
            {
                Position = futurePosition;
            }
        }

        private float ApplyDrag(float value, float drag)
        {
            if (value > 0)
            {
                value = MathHelper.Clamp(value, 0, value - drag);
            }
            if (value < 0)
            {
                value = MathHelper.Clamp(value, value + drag, 0);
            }
            return value;
        }

        private bool ValidateMovement(Vector3 futurePosition, Barriers barriers)
        {
            //Don't allow off-terrain driving
            if ((Math.Abs(futurePosition.X) > MaxRange) ||
                (Math.Abs(futurePosition.Z) > MaxRange))
                return false;

            BoundingSphere futureBoundingSphere = BoundingSphere;
            futureBoundingSphere.Center.X = futurePosition.X;
            futureBoundingSphere.Center.Z = futurePosition.Z;

            //Don't allow driving through a barrier
            if (CheckForBarrierCollision(futureBoundingSphere, barriers))
                return false;

            return true;
        }

        private bool CheckForBarrierCollision(BoundingSphere sphere, Barriers barriers)
        {
            if (barriers.Find(b => (b.Visible && b.ModelBoundingParts.Intersects(sphere))) != null)
                return true;
                
            return false;
        }

        public void Reset()
        {
            Position = new Vector3(0, BoundingSphere.Radius / 2f, 0);

            ForwardDirection = 0f;
            TurnImpulse = 0f;
            Thrust = 0f;
        }
    }
}